{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Streaming"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In production systems with user interaction, streaming output from LLMs greatly improves the user experience. Streaming allows you to build real-time systems that minimize the time to first token (TTFT) rather than waiting for the entire document to be completed before progessing.\n",
    "\n",
    "\n",
    "Guardrails natively supports validation for streaming output, supporting both synchronous and asynchronous approaches."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from rich import print\n",
    "import guardrails as gd\n",
    "from IPython.display import clear_output\n",
    "import time"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Streaming with a guard class can be done by setting the 'stream' parameter to 'True'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from guardrails.hub import CompetitorCheck\n",
    "\n",
    "prompt = \"Tell me about the Apple Iphone\"\n",
    "\n",
    "guard = gd.Guard().use(CompetitorCheck, [\"Apple\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">ValidationOutcome</span><span style=\"font-weight: bold\">(</span>\n",
       "    <span style=\"color: #808000; text-decoration-color: #808000\">call_id</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'14148119808'</span>,\n",
       "    <span style=\"color: #808000; text-decoration-color: #808000\">raw_llm_output</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'.'</span>,\n",
       "    <span style=\"color: #808000; text-decoration-color: #808000\">validation_summaries</span>=<span style=\"font-weight: bold\">[]</span>,\n",
       "    <span style=\"color: #808000; text-decoration-color: #808000\">validated_output</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'.'</span>,\n",
       "    <span style=\"color: #808000; text-decoration-color: #808000\">reask</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
       "    <span style=\"color: #808000; text-decoration-color: #808000\">validation_passed</span>=<span style=\"color: #00ff00; text-decoration-color: #00ff00; font-style: italic\">True</span>,\n",
       "    <span style=\"color: #808000; text-decoration-color: #808000\">error</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>\n",
       "<span style=\"font-weight: bold\">)</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n",
       "    \u001b[33mcall_id\u001b[0m=\u001b[32m'14148119808'\u001b[0m,\n",
       "    \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'.'\u001b[0m,\n",
       "    \u001b[33mvalidation_summaries\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m,\n",
       "    \u001b[33mvalidated_output\u001b[0m=\u001b[32m'.'\u001b[0m,\n",
       "    \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
       "    \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n",
       "    \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n",
       "\u001b[1m)\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fragment_generator = guard(\n",
    "    model=\"gpt-4o\",\n",
    "    messages=[\n",
    "        {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n",
    "        {\"role\": \"user\", \"content\": \"Tell me about LLM streaming APIs.\"},\n",
    "    ],\n",
    "    max_tokens=1024,\n",
    "    temperature=0,\n",
    "    stream=True,\n",
    ")\n",
    "\n",
    "\n",
    "for op in fragment_generator:\n",
    "    clear_output(wait=True)\n",
    "    print(op)\n",
    "    time.sleep(0.5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With streaming, not only do chunks from the LLM arrive as they are generated, but validation results can stream in real time as well.\n",
    "\n",
    "To do this, validators specify a chunk strategy. By default, validators wait until they have accumulated a sentence's worth of content from the LLM before running validation. Once they've run validation, they emit that result in real time. \n",
    "\n",
    "In practice, this means that you do not have to wait until the LLM has finished outputting tokens to access validation results, which helps you create smoother and faster user experiences. It also means that validation can run only on individual sentences, instead of the entire accumulated response, which helps save on costs for validators that require expensive inference.\n",
    "\n",
    "To access these validation results, use the error_spans_in_output helper method on Guard. This will provide an up to date list of all ranges of text in the output so far that have failed validation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "error_spans = guard.error_spans_in_output()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Async Streaming\n",
    "\n",
    "In cases where concurrent network calls are happening (many LLM calls!) it may be beneficial to use an asynchronous LLM client. Guardrails also natively supports asynchronous streaming calls.\n",
    "\n",
    "Learn more about async streaming [here](/docs/concepts/async_streaming)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "guard = gd.Guard()\n",
    "\n",
    "fragment_generator = await guard(\n",
    "    model=\"gpt-3.5-turbo\",\n",
    "    messages=[\n",
    "        {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n",
    "        {\"role\": \"user\", \"content\": \"Tell me about the streaming API of guardrails.\"},\n",
    "    ],\n",
    "    max_tokens=1024,\n",
    "    temperature=0,\n",
    "    stream=True,\n",
    ")\n",
    "\n",
    "\n",
    "async for op in fragment_generator:\n",
    "    clear_output(wait=True)\n",
    "    print(op)\n",
    "    time.sleep(0.5)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "litellm",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
